//-----------------------------------------------------------------------
// <copyright file="Tuple.cs" company="Rhys Parry">
//    Copyright Rhys Parry 2009
//    This file is released under the Apache 2.0 License
// </copyright>
//-----------------------------------------------------------------------
namespace Smarts.Core
{
    using System;

    /// <summary>
    /// Represents a tuple of two items.
    /// </summary>
    /// <typeparam name="TFirst">The type of the first.</typeparam>
    /// <typeparam name="TSecond">The type of the second.</typeparam>
    public class Tuple<TFirst, TSecond>
    {
        /// <summary>
        /// Backing field for the first item in the tuple.
        /// </summary>
        private readonly TFirst _first;

        /// <summary>
        /// Backing field for the second item in the tuple.
        /// </summary>
        private readonly TSecond _second;

        /// <summary>
        /// Initializes a new instance of the <see cref="Tuple&lt;TFirst, TSecond&gt;"/> class.
        /// </summary>
        /// <param name="first">The first.</param>
        /// <param name="second">The second.</param>
        public Tuple(TFirst first, TSecond second)
        {
            _first = first;
            _second = second;
        }

        /// <summary>
        /// Gets the first item in the tuple.
        /// </summary>
        /// <value>The first item in the tuple.</value>
        public TFirst First
        {
            get
            {
                return _first;
            }
        }

        /// <summary>
        /// Gets the second item in the tuple.
        /// </summary>
        /// <value>The second item in the tuple.</value>
        public TSecond Second
        {
            get
            {
                return _second;
            }
        }

        /// <summary>
        /// Gets a new tuple with the items swapped.
        /// </summary>
        /// <returns>A new tuple with the first and second items swapped.</returns>
        public Tuple<TSecond, TFirst> Swap()
        {
            return new Tuple<TSecond, TFirst>(_second, _first);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return "(" + _first + ", " + _second + ")";
        }
    }

    /// <summary>
    /// Static helper class to provide improved functionality for <see cref="Tuple{TFirst,TSecond}"/>
    /// </summary>
    public static class Tuple
    {
        /// <summary>
        /// Creates a tuple with the specified first and second values.
        /// </summary>
        /// <typeparam name="TFirst">The type of the first.</typeparam>
        /// <typeparam name="TSecond">The type of the second.</typeparam>
        /// <param name="first">The first.</param>
        /// <param name="second">The second.</param>
        /// <returns>A new tuple with the specified values.</returns>
        public static Tuple<TFirst, TSecond> Create<TFirst, TSecond>(TFirst first, TSecond second)
        {
            return new Tuple<TFirst, TSecond>(first, second);
        }

        /// <summary>
        /// Invokes the action using the arguments from the tuple.
        /// </summary>
        /// <typeparam name="TFirst">The type of the first.</typeparam>
        /// <typeparam name="TSecond">The type of the second.</typeparam>
        /// <param name="tuple">The tuple.</param>
        /// <param name="action">The action.</param>
        public static void Invoke<TFirst, TSecond>(this Tuple<TFirst, TSecond> tuple, Action<TFirst, TSecond> action)
        {
            action.Invoke(tuple.First, tuple.Second);
        }

        /// <summary>
        /// Invokes the function using the arguments from the tuple.
        /// </summary>
        /// <typeparam name="TFirst">The type of the first.</typeparam>
        /// <typeparam name="TSecond">The type of the second.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="tuple">The tuple.</param>
        /// <param name="function">The function.</param>
        /// <returns>The return value of the function.</returns>
        public static TResult Invoke<TFirst, TSecond, TResult>(this Tuple<TFirst, TSecond> tuple, Func<TFirst, TSecond, TResult> function)
        {
            return function.Invoke(tuple.First, tuple.Second);
        }
    }
}